package edu.wpi.cs509.serverlogic;

import java.util.ArrayList;
import java.util.List;

import edu.wpi.cs509.messages.client.MsgOpponentQuit;
import edu.wpi.cs509.network.Connection;
import edu.wpi.cs509.representation.Game;
import edu.wpi.cs509.representation.GameID;
import edu.wpi.cs509.representation.GameListItem;
import edu.wpi.cs509.representation.TeamColor;

/**
 * This class will contain the list of games.
 * 
 * It will act as a database of games and have methods such as "list all games",
 * "add this game to the lobby", "which game corresponds to this identifier"
 *  
 * @author abrocket
 *
 */
class Lobby {
	private ArrayList<Game> games;
	//HashMap<User,Game> userGameMap;

	public Lobby() {
		this.games = new ArrayList<Game>();
	}
	/**
	 * Looks up game based on game id and returns a reference to it
	 * 
	 * @param gid - game id being queried
	 * @return reference to the game with the given game id
	 */
	public Game lookupGame(long gid) {
		for (Game g: games) {
			if (g.getGameID().getGameIDLong() == gid) {
				return g;
			}
		}
		return null;
	}

	public void addGame(Game g) {
		games.add(g);
		// TODO Add game to User/Connection HashMap when available
	}
	
	/**
	 * Deletes a game.
	 * @param game The game object to delete
	 * @return true if successful, otherwise false
	 */
	public boolean deleteGame(Game game) {
		return games.remove(game);
	}
	
	/**
	 * Returns a list of games that users would be able to join
	 * These could be observable or joinable
	 * @return
	 */
	public List<GameListItem> getList() {
		ArrayList<GameListItem> ret = new ArrayList<GameListItem>();
		String redUser, blueUser;
		for (Game g : games) {
			if (g.isJoinable() || g.isObservable()) {
				Connection redPlayer = g.getPlayer(TeamColor.RED);
				if (redPlayer != null) {
					redUser = redPlayer.getUser();
				} else {
					redUser = null;
				}
				
				Connection bluePlayer = g.getPlayer(TeamColor.BLUE);
				if (bluePlayer != null) {
					blueUser = bluePlayer.getUser();
				} else {
					blueUser = null;
				}
				
				if(redUser == null && blueUser != null) {
					ret.add(new GameListItem(g.getGameID().getGameIDLong(), blueUser));
				} else if (redUser != null && blueUser == null) {
					ret.add(new GameListItem(g.getGameID().getGameIDLong(), redUser));
				} else if (redUser != null && blueUser != null) {
					ret.add(new GameListItem(g.getGameID().getGameIDLong(), redUser, blueUser));
				}
			}
		}
		return ret;
	}
	
	/**
	 * Attempts to join a user into a game.
	 * @param gid The game's identifier
	 * @param conn THe user's connection object
	 * @return color of player if successful, null otherwise
	 */
	public TeamColor joinGame(GameID gid, Connection conn) {
		Game game = lookupGame(gid.getGameIDLong());
		if (game == null) {
			System.err.println("Invalid game ID: " + gid);
			return null;
		}
		
		// TODO If both players have joined, begin the initial phase of the game
		System.out.println("Successfully added user " + conn.getUser() + "to game" + gid.getGameIDLong());
		return game.addPlayer(conn);
	}
	
	/**
	 * Attempts to remove a user from a game
	 * @param gid The game's identifier
	 * @param conn The user's connection object
	 * @return true if successful, false otherwise
	 */
	public boolean leaveGame(GameID gid, Connection conn) {
		Game game = lookupGame(gid.getGameIDLong());
		if (game == null) {
			System.err.println("Invalid game ID: " + gid);
			return false;
		}

		// TODO Check the state of the game. If the game has already begun this
		// will force a forfeit and stats need to be updated.
		
		if (!game.removePlayer(conn)) {
			System.err.println("User (" + conn.getUser() + ") cannot leave " +
						"game (" + gid + ")");
			return false;
		}
		
		if (game.isEmpty()) {
			// Game is now empty, destroy it
			System.out.println("Deleting game: " + gid);
			return deleteGame(game);
		}
		
		return true;
	}
	
	/**
	 * Adds an observer to a game
	 * @param gId The game's identifier
	 * @param conn The user's connection object
	 * @return true if successful, false otherwise
	 */
	public boolean observeGame(GameID gid, Connection conn) {
		Game game = lookupGame(gid.getGameIDLong());
		if(game == null) {
			System.err.println("Invalid game ID: " + gid);
			return false;
		}
		
		return game.addObserver(conn);
	}
	
	/**
	 * Removes an observer from the game
	 * @param gId The game's identifier
	 * @param conn The user's connection object
	 * @return true if successful, false otherwise
	 */
	public boolean stopObservingGame(GameID gid, Connection conn) {
		Game game = lookupGame(gid.getGameIDLong());
		if(game == null) {
			System.err.println("Invalid game ID: " + gid);
			return false;
		}
		
		if (!game.removeObserver(conn)) {
			System.err.println("Failed to remove observer: " + conn);
			return false;
		}
		
		if (game.isEmpty()) {
			// Game is now empty, destroy it
			System.out.println("Deleting game: " + gid);
			return deleteGame(game);
		}
		
		return true;
	}
	
	/**
	 * checks if a player is observing a game
	 * 
	 * @param conn - connection of the player verifying if they are observing
	 */
	public void checkForObserving(Connection conn){	
		// Loop through the list of games and each of their observer lists
		for (Game g : games) {
			//Get the list of observers
			List<Connection> observersList = g.getObservers();
			
			for (int i = 0; i < observersList.size(); i++) {
				if (conn == observersList.get(i)){
					// Found them watching a game, now exit the user
					if(!stopObservingGame(g.getGameID(), conn)){
						System.out.println("leaveGame failed");
					}
				}
			}
		}
	}
	
	/**
	 * Function to check if the connection is still playing games
	 * 
	 * @param conn - connection of the player verifying if they are observing
	 * @param terminatedEarly - boolean to determine whether or not the user quit or lost connection
	 */
	public void checkForPlaying(Connection conn, boolean terminatedEarly){
		for (Game g : games) {
			boolean playerFoundInGame = false;
			TeamColor oppColor = TeamColor.BOTH;
			if (conn == g.getPlayer(TeamColor.RED)){
				playerFoundInGame = true;
				oppColor = TeamColor.BLUE;
			}
			else if (conn == g.getPlayer(TeamColor.BLUE)){
				playerFoundInGame = true;
				oppColor = TeamColor.RED;
			}
			if (playerFoundInGame){
				// if true, then user terminated early, so notify other person in game
				if (terminatedEarly){
					MsgOpponentQuit userQuitMsg = new MsgOpponentQuit(/*g*/);
					g.getPlayer(oppColor).sendMessage(userQuitMsg);
				}
				
				// Found them playing, have them exit 
				if(!leaveGame(g.getGameID(), conn)){
					System.out.println("stopObservingGame failed");
				}
			}
		}
	}
	
}
